package com.tiger.controller;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.tiger.utils.RedisUtils;
import com.tiger.utils.UpdateUtils;
import com.tiger.mapper.base.UserMapper;
import com.tiger.pojo.base.User;
import com.tiger.pojo.chat.ChatListOfUserInfo;
import com.tiger.pojo.chat.DatetimeArr;
import com.tiger.pojo.chat.TextArr;
import io.minio.*;
import io.minio.http.Method;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

/**
 * @author HuXuehao (StudiousTiger)
 * @desc 这个类的作用是
 * @date 2022/3/14
 */
@Slf4j
@RestController
public class CacheController {
    @Autowired
    private RedisUtils redisUtil;
    @Autowired
    private UserMapper userMapper;

    @Autowired
    private HttpServletRequest httpServletRequest;

    private static final String WORK_BROWSE_COUNTS = "work_browse_counts";
    private static final String WORK_REPLIED_COUNTS = "work_replied_counts";
    private static final String TIPS = "tips";

    @Value("${minio.bucketNameOfChatImg}")
    private String bucketNameOfChatImg;
    @Value("${minio.endPoint}")
    private String endPoint;

    @Autowired
    private MinioClient minioClient;
    @Autowired
    private UpdateUtils updateUtils;

    /**
     * 获取、添加、更新 文章的浏览数
     * @param articleId
     * @return
     */
    @RequestMapping(value = "/cache/browseCounts/{articleId}", name = "获取、添加、更新 文章的浏览数/user")
    public String workBrowseCounts(@PathVariable String articleId){
        try {
            boolean hexist = redisUtil.hexist(WORK_BROWSE_COUNTS, articleId);
            if (hexist) {
                redisUtil.hincr(WORK_BROWSE_COUNTS, articleId,1);
            } else {
                redisUtil.hset(WORK_BROWSE_COUNTS, articleId,"1");
            }
            return (String) redisUtil.hget(WORK_BROWSE_COUNTS, articleId);
        } catch (Exception e) {
            log.error(e.toString());
            return "0";
        }
    }

    /**
     * 获取、添加、更新 文章的回复数
     * @param articleId
     * @return
     */
    @RequestMapping(value = "/cache/workRepliedCounts/{articleId}", name = "获取、添加、更新 文章的回复数/user")
    public String workRepliedCounts(@PathVariable String articleId){
        try {
            boolean hexist = redisUtil.hexist(WORK_REPLIED_COUNTS, articleId);
            if (hexist) {
                redisUtil.hincr(WORK_REPLIED_COUNTS, articleId,1);
            } else {
                redisUtil.hset(WORK_REPLIED_COUNTS, articleId,"1");
            }
            return (String) redisUtil.hget(WORK_REPLIED_COUNTS, articleId);
        } catch (Exception e) {
            log.error(e.toString());
            return "0";
        }
    }

    /**
     * 获取首页的励志句子（tips）
     * @return
     */
    @RequestMapping(value = "/cache/getRandTip", name = "获取首页的励志句子（tips）/user&admin")
    public String getRandTip() {
        try {
            // redis 中的 Srandmember 不好用，就能使用下面的方法了
            Set<Object> tips = redisUtil.sGet(TIPS);  // 取出全部
            ArrayList<Object> tipsList = new ArrayList(tips);  // 转成list
            int randomIndex = new Random().nextInt(tipsList.size());  // 设置随机索引

            return (String) tipsList.get(randomIndex);
        } catch (Exception e) {
            log.error(e.toString());
            return "抱歉，缓存服务器异常，暂时没有数据，我们正在排查中。";
        }
    }

    /**
     * 初始化聊天记录
     * @param targetUserId 被聊天用户ID
     * @return
     */
    @RequestMapping(value = "/cache/initChatRecordOfText/{targetUserId}", name = "初始化聊天记录/user")
    public String initChatRecordOfText(@PathVariable String targetUserId){
        try {
            String resultID = httpServletRequest.getSession().getAttribute("userId") + targetUserId + "chattxt";
            List<Object> txtList = redisUtil.lGet(resultID, 0, -1);

            // 更新聊天记录中的图片链接
            List<String> chatList = updateImgOfChat(txtList);

            return JSON.toJSONString(chatList);
        } catch (Exception e) {
            e.printStackTrace();
            return "[]";
        }

    }
    /**
     * 更新聊天记录中的图片链接
     * @param list
     * @return
     */
    private List<String> updateImgOfChat(List<Object> list) throws Exception{
        // 1. 将Object类型的list转化成String类型的List
        List<String> chatList = obj2StrOfList(list);

        // 2. 过滤list中的图片名
        Map<Integer, String> imgNameMap = filterImgName(chatList);

        // 3. 重新获取图片url
        for (Integer key : imgNameMap.keySet()) {
            // 图片名
            String imgName = imgNameMap.get(key);
            // 重新获取图片链接
            String imgURL = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs
                            .builder()
                            .method(Method.GET)
                            .bucket(bucketNameOfChatImg)
                            .object(imgName)
                            .build());

            imgNameMap.put(key,imgURL);
        }

        // 4. 更新list的图片url
        List<String> newChatList = rebuild(chatList, imgNameMap);

        return newChatList;
    }
    /**
     * 将Object类型的list转化成String类型的List
     * @param list
     * @return
     */
    private List<String> obj2StrOfList(List<Object> list) {
        List<String> strings = new ArrayList<>();
        for (Object o : list) {
            strings.add(o.toString());
        }
        return strings;
    }
    /**
     * 过滤出聊天记录中的图片名，之所以使用map是因为map的key的对应的聊天记录list的索引，方便重构聊天记录
     * @param chatList
     * @return 索引为key,图片名为value的map
     */
    private Map<Integer, String> filterImgName(List<String> chatList){
        Map<Integer, String> imgNameMap = new HashMap<Integer, String>();
        for (int i = 0; i < chatList.size(); i++) {
            // 因为编辑文本限制为300字符，而图片的链接为328个字符
            if (chatList.get(i).length() > 300) {
                String[] arr = chatList.get(i).split("\\?")[0].split("/");
                // 判断是否为图片名
                if (isImg(arr[arr.length-1])) {
                    imgNameMap.put(i, arr[arr.length-1]);
                } else {
                    continue;
                }
            } else {
                continue;
            }
        }
        return imgNameMap;
    }
    /**
     * 重构聊天记录
     * @param chatList 旧的聊天记录
     * @param newImgUrl 新的图片地址
     * @return 新的聊天记录
     */
    private List<String> rebuild(List<String> chatList, Map<Integer, String> newImgUrl) {
        for (Integer index : newImgUrl.keySet()) {
            chatList.set(index, newImgUrl.get(index));
        }

        return chatList;
    }
    /**
     * 判断 字符串是不是图片
     * @param imgName
     * @return
     */
    private boolean isImg(String imgName){
        String str = "bmp,jpg,png,tif,gif,pcx,tga,exif,fpx,svg,psd,cdr,pcd,dxf,ufo,eps,ai,raw,WMF,webp,avif,apng";
        String[] imgEnd = str.split(",");
        for (String end : imgEnd) {
            if (imgName.contains(end)){
                return true;
            } else {
                continue;
            }
        }
        return false;
    }

    /**
     * 初始化聊天记录（时间）
     * @param targetUserId 被聊天用户ID
     * @return
     */
    @RequestMapping(value = "/cache/initChatRecordOfDatetime/{targetUserId}", name = "初始化聊天记录（时间）/user")
    public String initChatRecordOfDatetime(@PathVariable String targetUserId){
        try {
            String resultID = httpServletRequest.getSession().getAttribute("userId") + targetUserId + "chatdatetime";
            List<Object> datetimeList = redisUtil.lGet(resultID, 0, -1);

            return JSON.toJSONString(datetimeList);
        } catch (Exception e) {
            e.printStackTrace();
            return "[]";
        }
    }

    /**
     * 持久化化聊天记录（文本）
     *@param targetUserId 被聊天用户ID
     * @return
     */
    @RequestMapping(value = "/cache/saveChatOfText/{targetUserId}",name = "持久化化聊天记录（文本）/user",method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public String saveChatOfText(@PathVariable String targetUserId, @RequestBody JSONObject jsonParam){
        try {
            String resultID = httpServletRequest.getSession().getAttribute("userId") + targetUserId + "chattxt";
            TextArr textArr = JSON.parseObject(String.valueOf(jsonParam), TextArr.class);
            System.out.println(textArr.getNews().toString());

            List<String> chatTextOfList = Arrays.asList(textArr.getNews());

            redisUtil.del(resultID);
            for (String res : chatTextOfList) {
                redisUtil.lSet(resultID, res);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 持久化聊天记录（时间）
     * @param targetUserId 被聊天用户ID
     * @return
     */
    @RequestMapping(value = "/cache/saveChatOfDatetime/{targetUserId}", name = "持久化聊天记录（时间）/user", method = RequestMethod.POST, produces = "application/json;charset=UTF-8")
    public String publishError(@PathVariable String targetUserId, @RequestBody JSONObject jsonParam){
        try {
            String resultID = httpServletRequest.getSession().getAttribute("userId") + targetUserId + "chatdatetime";
            DatetimeArr datetimeArr = JSON.parseObject(String.valueOf(jsonParam), DatetimeArr.class);

            List<String> chatDateTimeOfList = Arrays.asList(datetimeArr.getDatetime());

            redisUtil.del(resultID);
            for (String res : chatDateTimeOfList) {
                redisUtil.lSet(resultID, res);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 插入新的聊天用户到聊天列表中，并返回所有的列表
     * @param chattedUserId
     * @return
     */
    private static final String NEW_NEWS_MAP_NAME = "new_news_map_name";
    @RequestMapping(value = "/cache/chatList/{chattedUserId}", name = "插入新的聊天用户到聊天列表中/user")
    public String getChatList(@PathVariable String chattedUserId){
        String currentUser = (String) httpServletRequest.getSession().getAttribute("userId");
        String chatListOfUser = currentUser + "chatList";
        try {
            if (!"undefined".equals(chattedUserId)){
                boolean sExist = redisUtil.lExist(chatListOfUser, chattedUserId);
                if (!sExist) {
                    redisUtil.lSetFromLeft(chatListOfUser,chattedUserId);
                }
            }
            // 获取聊天列表中对应的用户的Id，并转换成list
            List<Object> chatList = redisUtil.lGet(chatListOfUser, 0, -1);

            ArrayList<ChatListOfUserInfo> userInfos = new ArrayList<>();
            for (Object userId : chatList) {
                if (userId == null || "".equals(userId) || "undefined".equals(userId)){
                    continue;
                }
                ChatListOfUserInfo userInfo = new ChatListOfUserInfo();
                User user = userMapper.selectById(userId.toString());
                userInfo.setUserId(userId.toString());
                userInfo.setUserNickname(user.getUserNickname());
                userInfo.setUserHeadPhoto(user.getUserHeadPortrait());

                List<Object> list = redisUtil.lGet(currentUser + userId.toString() + "chattxt", 0L, -1L);
                if (list.size() == 0) {
                    userInfo.setLatestNew("暂无消息...");
                } else {
                    for (int i=list.size()-1; i>=0; i--){
                        if ("".equals(list.get(i).toString())){
                            continue;
                        } else {
                            if (list.get(i).toString().contains("http://192.168.174.138:9000/chatimg")){
                                userInfo.setLatestNew("[图片]");
                            } else if(list.get(i).toString().contains("icon-")){
                                userInfo.setLatestNew("[表情]");
                            } else {
                                userInfo.setLatestNew(list.get(i).toString());
                            }
                            break;
                        }
                    }
                }

                String newNewsTips = (String)redisUtil.hget(NEW_NEWS_MAP_NAME, currentUser + userId);
                userInfo.setNewNewTips(newNewsTips);

                userInfos.add(userInfo);
            }
            for (ChatListOfUserInfo userInfo : userInfos) {
                userInfo.setUserHeadPhoto(
                        updateUtils.getNewHeadPhotoURL(
                                userInfo.getUserHeadPhoto()
                        )
                );
            }
            return JSON.toJSONString(userInfos);
        } catch (Exception e) {
            e.printStackTrace();
            return "false";
        }
    }

    /**
     * 取消新消息提示
     * @param chattedUserId
     * @return
     */
    @RequestMapping(value = "/cache/cancelNewNewsTips/{chattedUserId}", name = "取消新消息提示/user")
    public String cancelNewNewsTips(@PathVariable String chattedUserId){
        String currentUserId = (String) httpServletRequest.getSession().getAttribute("userId");
        try {
            redisUtil.hset(NEW_NEWS_MAP_NAME, currentUserId + chattedUserId,"0");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 新消息提示+1
     * @param sendNewsUserId
     * @return
     */
    @RequestMapping(value = "/cache/setNewNewsTips/{sendNewsUserId}", name = "新消息提示+1/user")
    public String setNewNewsTips(@PathVariable String sendNewsUserId){
        String currentUserId = (String) httpServletRequest.getSession().getAttribute("userId");
        try {
            String newNewsTips = (String)redisUtil.hget(NEW_NEWS_MAP_NAME, currentUserId + sendNewsUserId);

            int count = Integer.parseInt(newNewsTips);
            redisUtil.hset(NEW_NEWS_MAP_NAME, currentUserId + sendNewsUserId,count+1+"");
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 删除聊天记录
     * @param deletedUserId
     * @return
     */
    @RequestMapping(value = "/cache/delChatRecord/{deletedUserId}", name = "删除聊天记录/user")
    public String delChatRecord(@PathVariable String deletedUserId) {
        String currentUser = (String) httpServletRequest.getSession().getAttribute("userId");

        String chatListOfUser = currentUser + "chatList";
        String delChatRecordOfTxt = currentUser + deletedUserId + "chattxt";
        String delChatRecordOfDatetime = currentUser + deletedUserId + "chatdatetime";
        try {
            long delCount = redisUtil.lRemove(chatListOfUser, 1, deletedUserId);
            if (delCount > 0){
                redisUtil.del(delChatRecordOfTxt,delChatRecordOfDatetime);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    @PostMapping(value = "/cache/image", name = "上传图片到服务器/user")
    public String chatImage(@RequestParam(name = "file", required = false) MultipartFile file){
        if (file == null){
            return "false";
        }
        String fileName = UUID.randomUUID()+".jpg";
        return putImgAndGetUrl(file, fileName);
    }
    private String putImgAndGetUrl(MultipartFile file, String fileName){
        InputStream in = null;
        try {
            // 判断目标 bucket 是否存在
            boolean exit = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketNameOfChatImg).build());
            if (!exit){
                // 创建名为 bucketName 的 bucket
                minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketNameOfChatImg).build());
            }

            in = file.getInputStream();
            // 上传图片
            minioClient.putObject(
                    PutObjectArgs.builder()
                            .bucket(bucketNameOfChatImg)
                            .object(fileName)
                            .stream(in,file.getSize(),-1)
                            .build()
            );

//             获取头像路径
            String url = minioClient.getPresignedObjectUrl(
                    GetPresignedObjectUrlArgs
                            .builder()
                            .method(Method.GET)
                            .bucket(bucketNameOfChatImg)
                            .object(fileName)
                            .build());
            return url;
        } catch (Exception e) {
            e.printStackTrace();
            return "false";
        } finally {
            // 关闭流
            if (in != null){
                try {
                    in.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}